import pandas as pd
import os
from file_utils import read_excel, save_excel

class 异地数据转换器:
    def __init__(self):
        """初始化异地数据转换器"""
        self.source_df = None
        self.target_df = None
        self.result_df = None
        # 目标模板相对路径（规则5）
        self.target_template_path = "templates\\JY结算主记录.xlsx"
        
    def load_source_table(self, file_path):
        """加载源表格（规则1）"""
        try:
            self.source_df = read_excel(file_path)
            print(f"成功加载源表格: {file_path}，共 {len(self.source_df)} 行")
            return True
        except Exception as e:
            print(f"加载源表格失败: {str(e)}")
            return False
    
    def load_target_template(self):
        """加载目标模板表格（规则2）"""
        try:
            # 处理相对路径
            if not os.path.isabs(self.target_template_path):
                target_path = os.path.abspath(self.target_template_path)
            else:
                target_path = self.target_template_path
                
            if not os.path.exists(target_path):
                print(f"目标模板不存在: {target_path}")
                return False
                
            self.target_df = read_excel(target_path)
            print(f"成功加载目标模板: {target_path}，共 {len(self.target_df.columns)} 个字段")
            return True
        except Exception as e:
            print(f"加载目标模板失败: {str(e)}")
            return False
    
    def _map_fields(self):
        """字段匹配映射 - 使用固定映射关系（规则1、6）"""
        if self.source_df is None or self.target_df is None:
            raise ValueError("源表格或目标模板未加载")
            
        target_fields = self.target_df.columns.tolist()
        source_fields = self.source_df.columns.tolist()
        
        # 固定字段映射规则（规则6）
        fixed_mapping = {
            # 源字段: 目标字段（可以是单个字段或多个字段列表）
            "就诊ID": "记账流水号",
            "结算ID": "结算编号",
            "医疗费总额": "费用总额",
            "个账支付": "个人账户支付总额",
            "基金支付": "社保基金支付总额",
            "个人现金支付": "现金支付",
            "结算时间": "经办时间",
            "人员编号": "个人编码",
            "参保地编码": "备注",
            "清算类别": ["原始清算类别", "医疗类别"]  # 一个源字段对应多个目标字段
        }
        
        # 创建字段映射字典
        field_mapping = {}
        
        # 处理固定映射
        for source_field, target_field in fixed_mapping.items():
            # 处理多目标字段的情况
            if isinstance(target_field, list):
                for tf in target_field:
                    if tf in target_fields and source_field in source_fields:
                        field_mapping[tf] = source_field
            else:
                if target_field in target_fields and source_field in source_fields:
                    field_mapping[target_field] = source_field
        
        # 规则1: 保留目标模板中与源表格字段名完全匹配的字段
        for field in target_fields:
            if field in source_fields and field not in field_mapping:
                field_mapping[field] = field
                    
        return field_mapping
    
    def _process_numeric_fields(self, df):
        """处理纯数字且长度超过6的值为文本格式（规则4）"""
        def convert_long_numbers(value):
            # 检查是否为数字类型且不是NaN
            if pd.api.types.is_numeric_dtype(type(value)) and not pd.isna(value):
                str_value = str(int(value)) if isinstance(value, float) and value.is_integer() else str(value)
                if len(str_value) > 6:
                    return f"'{str_value}"  # 添加单引号使其成为文本格式
            return value
            
        # 对所有单元格应用处理函数
        return df.applymap(convert_long_numbers)
    
    def _parse_id_card(self, id_card):
        """从身份证号码解析性别和出生日期（规则7、8）"""
        if not id_card or len(str(id_card)) not in [15, 18]:
            return None, None
            
        id_str = str(id_card).strip()
        gender = None
        birth_date = None
        
        try:
            # 解析出生日期（规则8：格式为1983/5/25）
            if len(id_str) == 18:
                year, month, day = id_str[6:10], id_str[10:12], id_str[12:14]
            else:  # 15位身份证
                year, month, day = f"19{id_str[6:8]}", id_str[8:10], id_str[10:12]
                
            # 格式化日期为1983/5/25格式（去除前导零）
            birth_date = f"{year}/{int(month)}/{int(day)}"
            
            # 解析性别 (规则7)
            if len(id_str) == 18:
                gender_code = int(id_str[16])
            else:
                gender_code = int(id_str[14])
                
            gender = "男" if gender_code % 2 == 1 else "女"
            
        except (ValueError, IndexError):
            pass
            
        return gender, birth_date
    
    def _process_special_fields(self):
        """处理特殊字段规则（规则7-12及新增规则）"""
        # 规则7和8: 从证件号码解析性别和出生日期
        if "证件号码" in self.result_df.columns:
            # 解析性别和出生日期
            id_info = self.result_df["证件号码"].apply(
                lambda x: self._parse_id_card(x)
            )
            
            # 提取性别和出生日期
            if "性别" in self.result_df.columns:
                self.result_df["性别"] = id_info.apply(lambda x: x[0] if x[0] else self.result_df["性别"])
            
            if "出生日期" in self.result_df.columns:
                self.result_df["出生日期"] = id_info.apply(lambda x: x[1] if x[1] else self.result_df["出生日期"])
        
        # 规则9: 挂钩自付部分、符合范围部分自动填充为0
        zero_fields = ["挂钩自付部分", "符合范围部分"]
        for field in zero_fields:
            if field in self.result_df.columns:
                self.result_df[field] = 0
        
        # 规则10: 清算分中心自动填充为510100
        if "清算分中心" in self.result_df.columns:
            self.result_df["清算分中心"] = 510100
        
        # 规则11: 清算标志自动填充为0
        if "清算标志" in self.result_df.columns:
            self.result_df["清算标志"] = 0
        
        # 规则12: 医疗类别名称自动填充为药店购药
        if "医疗类别名称" in self.result_df.columns:
            self.result_df["医疗类别名称"] = "药店购药"
        
        # 新增规则: 清算类别为N41、41时，异地交易标志填充为1
        if "清算类别" in self.result_df.columns and "异地交易标志" in self.result_df.columns:
            # 转换为字符串并去除空格，处理可能的类型差异
            清算类别_str = self.result_df["清算类别"].astype(str).str.strip()
            # 匹配N41或41，不区分大小写
            mask = 清算类别_str.isin(["N41", "41"])
            # 排除9950的情况
            mask = mask & ~清算类别_str.isin(["9950"])
            # 设置异地交易标志为1
            self.result_df.loc[mask, "异地交易标志"] = 1
    
    def generate_异地_data(self, output_path=None):
        """生成异地数据表格"""
        if not self.load_target_template():
            raise FileNotFoundError("目标模板加载失败，请确保templates\\JY结算主记录.xlsx存在")
            
        field_mapping = self._map_fields()
        target_fields = self.target_df.columns.tolist()
        
        # 创建新表格，保留目标字段顺序（规则3）
        new_rows = []
        for _, row in self.source_df.iterrows():
            new_row = {}
            for field in target_fields:
                if field in field_mapping and field_mapping[field] in row:
                    new_row[field] = row[field_mapping[field]]
                else:
                    new_row[field] = None  # 字段不匹配则为空
            new_rows.append(new_row)
            
        self.result_df = pd.DataFrame(new_rows, columns=target_fields)
        
        # 处理特殊字段规则（自动填充和解析）
        self._process_special_fields()
        
        # 处理长数字转文本
        self.result_df = self._process_numeric_fields(self.result_df)
        
        # 保存结果
        if output_path:
            try:
                save_excel(self.result_df, output_path)
                print(f"异地数据转换完成，保存至: {output_path}")
                return True
            except Exception as e:
                print(f"保存文件失败: {str(e)}")
                return False
        return True

def convert_异地_data(source_file_path, output_path=None):
    """外部调用接口：转换异地数据"""
    converter = 异地数据转换器()
    if not converter.load_source_table(source_file_path):
        return False
        
    try:
        if output_path:
            return converter.generate_异地_data(output_path)
        else:
            # 生成默认输出路径
            dir_name, file_name = os.path.split(source_file_path)
            new_file_name = "异地数据_" + file_name
            default_output = os.path.join(dir_name, new_file_name)
            return converter.generate_异地_data(default_output)
    except Exception as e:
        print(f"转换异地数据失败: {str(e)}")
        return False